<?php

namespace LhpAmis\Traits;

use app\admin\model\SystemUploadfile;
use LhpAmis\thinkphp\Provider\File;
use LhpAmis\util\AmisConfig;
use LhpAmis\util\AmisUploadValidator;
use think\Exception;
use think\facade\Event;
use think\facade\Cache;
use think\facade\Request;
use think\facade\Filesystem;
use think\file\UploadedFile;
use think\exception\ValidateException;

trait UploadTrait
{
    // 本地文件驱动名称
    protected $diskDriver = 'local';

    // 分片上传临时目录
    private $tempDir = 'temp/chunks/';

    // 完整文件保存目录
    private $saveDir = 'uploads/';

    // 支持的上传类型
    private $allowedTypes = ['file', 'image'];

    // 上传会话过期时间（秒）
    private $sessionExpire = 3600;

    /**
     * 统一上传处理入口（仅保留普通上传功能）
     */
    protected function file()
    {
        // try {
        // 检测上传的文件
        $file = $this->request->file('file') ?: $this->request->file('image');
        if (empty($file)) {
            throw new \Exception('请选择文件', 1);

        }

        // 通过文件本身判断类型
        $type = $this->detectFileType($file);
        if (!in_array($type, $this->allowedTypes)) {
            throw new \Exception('不支持的文件类型', 1);
        }
        // 验证文件
        AmisUploadValidator::validate($type, ['file' => $file]);
        //获取上传类型
        $this->diskDriver = $this->request->param('upload_type', sysconfig('upload', 'upload_type'));
        //图片转为其他格式
        $imageToFormat = $this->request->param('imageToFormat', AmisConfig::get('imageToFormat'));

        return (array) $this->processUploadedFile($file, $imageToFormat);


        // } catch (ValidateException $e) {
        //     return $this->error($e->getMessage());
        // } catch (Exception $e) {
        //     return $this->error('上传失败：' . $e->getMessage());
        // }
    }
    /**
     * 根据文件内容检测文件类型
     */
    private function detectFileType(UploadedFile $file)
    {
        // 获取文件MIME类型
        $mimeType = $file->getOriginalMime();

        // 判断是否为图片
        if (preg_match('/^image\//', $mimeType)) {
            return 'image';
        }

        // 如需支持视频，可添加视频类型判断
        if (preg_match('/^video\//', $mimeType)) {
            return 'video';
        }

        // 默认为文件
        return 'file';
    }
    /**
     * 处理已上传的文件
     * @param \think\file\UploadedFile $file
     * @param mixed $imageToFormat
     * @throws \think\Exception
     * @return array
     */
    protected function processUploadedFile(UploadedFile $file, $imageToFormat = null): array
    {
        try {
            // 1. 触发文件处理事件（用于二次加工）
            $eventData = ['file' => $file, 'processed' => false, 'imageToFormat' => $imageToFormat, 'diskDriver' => $this->diskDriver];
            $eventResult = Event::trigger('upload', $eventData);

            // 检查事件返回是否有效
            if (!empty($eventResult) && is_array($eventResult[0])) {
                $eventData = $eventResult[0];

                // 验证事件返回的文件对象
                if (isset($eventData['file']) && $eventData['processed'] == true && $eventData['file'] instanceof UploadedFile) {
                    // 6. 返回结果
                    return (array) $eventData['info'];
                }
            } else {
                // 5. 准备文件信息并保存到数据库
                return (array) $this->saveFileToDatabase($file, $this->diskDriver, $imageToFormat);
            }


        } catch (Exception $e) {
            throw new Exception('处理上传文件失败：' . $e->getMessage());
        }
        return ['url' => ''];
    }

    /**
     * 检查文件哈希是否已存在（默认返回false）
     */
    private function findFileByHash(string $fileHash)
    {
        return false;
    }

    /**
     * 保存文件信息到数据库（默认返回文件信息，不执行数据库操作）
     * @param \think\file\UploadedFile $file
     * @param string $disk
     * @param string|null $imageToFormat
     * @return array{url: mixed, value: mixed|\think\Response}
     */
    protected function saveFileToDatabase(UploadedFile $file, string $disk, string $imageToFormat = null)
    {
        if ($imageToFormat) {
            //添加所需要转换的格式代码
            $file = (new File($file->getPathname(), $file->getOriginalName()))->convertImage($imageToFormat);

        }

        // 4. 统一存储文件
        $savePath = Filesystem::disk($this->diskDriver)->putFile('upload', $file);

        $fileInfo = [
            'disk' => $disk,
            'mime_type' => $file->getOriginalMime(),
            'sha1' => $file->hash('sha1'),// 默认为原始文件哈希
            'size' => $file->getSize(),
            'original_name' => $file->getOriginalName(),
            'ext' => strtolower($file->getOriginalExtension()),
            'url' => '/' . Filesystem::disk($this->diskDriver)->putFile('upload', $file),
            'width' => 0,
            'height' => 0,
        ];

        if ($filesite = self::checkImageAndGetSize(root_path() . 'public' . DIRECTORY_SEPARATOR . $savePath)) {
            $fileInfo['width'] = $filesite['width'];
            $fileInfo['height'] = $filesite['height'];
        }
        return $fileInfo;
    }
    /**
     * 检测文件是否为图片并获取尺寸
     * 使用示例
     * $file = 'test.jpg';
     * $result = checkImageAndGetSize($file);
     * if ($result) {
     *     echo "是图片，宽度：{$result['width']}px，高度：{$result['height']}px，类型：{$result['mime']}";
     * } else {
     *     echo "不是有效的图片文件";
     * }
     * @param string $filePath 文件路径
     * @return array|false 成功返回['width' => 宽, 'height' => 高, 'mime' => MIME类型]，失败返回false
     */
    static public function checkImageAndGetSize($filePath)
    {
        // 检查文件是否存在
        if (!file_exists($filePath)) {
            return false;
        }

        // 获取图片信息
        $imageInfo = getimagesize($filePath);

        // 不是有效的图片
        if (!$imageInfo) {
            return false;
        }

        // 返回尺寸和MIME类型
        return [
            'width' => $imageInfo[0],    // 宽度
            'height' => $imageInfo[1],   // 高度
            'mime' => $imageInfo['mime'] // MIME类型（如image/jpeg）
        ];
    }

    /**
     * 初始化分片上传
     */
    protected function startChunkApi(Request $request)
    {
        try {
            // 获取上传类型和文件名
            $type = $request->post('type', 'file');
            $filename = $request->post('filename');

            if (!in_array($type, $this->allowedTypes)) {
                return $this->error('不支持的上传类型');
            }

            if (empty($filename)) {
                return $this->error('缺少必要参数：filename');
            }

            // 生成唯一的uploadId
            $uploadId = uniqid('upload_');

            // 生成存储key
            $key = $this->generateStorageKey($filename, $type);

            // 记录上传会话信息
            $uploadSession = [
                'upload_id' => $uploadId,
                'key' => $key,
                'filename' => $filename,
                'type' => $type,
                'total_parts' => 0,
                'received_parts' => [],
                'created_at' => time()
            ];

            // 保存会话信息到缓存
            Cache::set('upload_session_' . $uploadId, $uploadSession, $this->sessionExpire);

            // 创建临时目录
            $tempFilePath = $this->tempDir . $uploadId . '/';
            Filesystem::disk($this->diskDriver)->makeDirectory($tempFilePath);

            return $this->success('分片上传初始化成功', [
                'uploadId' => $uploadId,
                'key' => $key
            ]);

        } catch (Exception $e) {
            return $this->error('初始化失败：' . $e->getMessage());
        }
    }

    /**
     * 接收分片上传
     */
    protected function chunkApi(Request $request)
    {
        try {
            // 获取上传参数
            $uploadId = $request->post('uploadId');
            $key = $request->post('key');
            $partNumber = $request->post('partNumber', 0, 'intval');
            $totalParts = $request->post('totalParts', 0, 'intval');
            $file = $request->file('file');

            if (empty($uploadId) || empty($key) || $partNumber <= 0 || empty($file)) {
                return $this->error('缺少必要参数');
            }

            // 验证上传会话
            $session = Cache::get('upload_session_' . $uploadId);
            if (empty($session) || $session['key'] !== $key) {
                return $this->error('无效的上传会话');
            }

            // 更新总片数（如果有）
            if ($totalParts > 0 && $session['total_parts'] === 0) {
                $session['total_parts'] = $totalParts;
                Cache::set('upload_session_' . $uploadId, $session, $this->sessionExpire);
            }

            // 保存当前分片
            $tempFilePath = $this->tempDir . $uploadId . '/';
            $chunkFileName = $tempFilePath . 'part_' . $partNumber;

            // 移动上传的分片到临时目录
            $success = move_uploaded_file(
                $file->getRealPath(),
                Filesystem::disk($this->diskDriver)->path($chunkFileName)
            );

            if (!$success) {
                throw new Exception('分片保存失败');
            }

            // 计算分片的eTag (内容哈希)
            $eTag = hash_file('sha1', Filesystem::disk($this->diskDriver)->path($chunkFileName));

            // 更新会话信息，记录已接收的分片
            $session['received_parts'][$partNumber] = [
                'part_number' => $partNumber,
                'size' => $file->getSize(),
                'eTag' => $eTag
            ];
            Cache::set('upload_session_' . $uploadId, $session, $this->sessionExpire);

            return $this->success('分片上传成功', [
                'eTag' => $eTag
            ]);

        } catch (Exception $e) {
            return $this->error('分片上传失败：' . $e->getMessage());
        }
    }

    /**
     * 完成分片上传，合并分片
     */
    protected function finishChunkApi(Request $request)
    {
        try {
            // 获取上传参数
            $uploadId = $request->post('uploadId');
            $key = $request->post('key');
            $filename = $request->post('filename');
            $partList = $request->post('partList', []);

            if (empty($uploadId) || empty($key) || empty($filename) || empty($partList)) {
                return $this->error('缺少必要参数');
            }

            // 验证上传会话
            $session = Cache::get('upload_session_' . $uploadId);
            if (empty($session) || $session['key'] !== $key) {
                return $this->error('无效的上传会话');
            }

            // 验证所有分片都已上传
            $expectedParts = array_column($partList, 'partNumber');
            $receivedParts = array_keys($session['received_parts']);

            if (count(array_diff($expectedParts, $receivedParts)) > 0) {
                return $this->error('部分分片未上传完成');
            }

            // 验证eTag是否匹配
            foreach ($partList as $part) {
                $partNumber = $part['partNumber'];
                $expectedETag = $part['eTag'];

                if (
                    !isset($session['received_parts'][$partNumber]) ||
                    $session['received_parts'][$partNumber]['eTag'] !== $expectedETag
                ) {
                    return $this->error('分片校验失败，可能文件已损坏');
                }
            }

            // 合并分片
            $mergedFile = $this->mergeChunks($uploadId, $filename);
            if (!$mergedFile) {
                return $this->error('文件合并失败');
            }

            // 验证合并后的文件
            $type = $session['type'];
            $fileField = $type === 'image' ? 'image' : 'file';
            AmisUploadValidator::validate($type, [$fileField => $mergedFile]);
            // 处理合并后的文件
            $totalSize = array_sum(array_column($session['received_parts'], 'size'));
            if ($mergedFile->getSize() == $totalSize) {
                $response = $this->processUploadedFile($mergedFile, $type);
                return $response;
            }

            // 清理会话
            Cache::delete('upload_session_' . $uploadId);
        } catch (ValidateException $e) {
            return $this->error($e->getMessage());
        } catch (Exception $e) {
            return $this->error('完成上传失败：' . $e->getMessage());
        }
    }


    /**
     * 合并分片文件
     */
    private function mergeChunks($uploadId, $filename)
    {
        $tempFilePath = $this->tempDir . $uploadId . '/';
        $savePath = $this->saveDir . $filename;

        // 创建最终文件
        $fp = fopen(Filesystem::disk($this->diskDriver)->path($savePath), 'wb');
        if (!$fp) {
            return false;
        }

        // 获取所有分片并按序号排序
        $chunks = [];
        if (Filesystem::disk($this->diskDriver)->has($tempFilePath)) {
            $files = Filesystem::disk($this->diskDriver)->files($tempFilePath);
            foreach ($files as $file) {
                $partNumber = (int) str_replace('part_', '', basename($file));
                $chunks[$partNumber] = $file;
            }
        }

        // 确保按序号排序
        ksort($chunks);

        // 按顺序合并分片
        foreach ($chunks as $partNumber => $chunkPath) {
            if (Filesystem::disk($this->diskDriver)->has($chunkPath)) {
                fwrite($fp, file_get_contents(Filesystem::disk($this->diskDriver)->path($chunkPath)));
                Filesystem::disk($this->diskDriver)->delete($chunkPath); // 删除已合并的分片
            }
        }
        fclose($fp);
        Filesystem::disk($this->diskDriver)->deleteDirectory($tempFilePath); // 删除临时目录

        // 创建UploadedFile对象返回
        return new UploadedFile(
            Filesystem::disk($this->diskDriver)->path($savePath),
            $filename,
            mime_content_type(Filesystem::disk($this->diskDriver)->path($savePath)),
            filesize(Filesystem::disk($this->diskDriver)->path($savePath)),
            UPLOAD_ERR_OK
        );
    }

    /**
     * 生成存储key
     */
    private function generateStorageKey($filename, $type)
    {
        $ext = pathinfo($filename, PATHINFO_EXTENSION);
        $randomString = bin2hex(random_bytes(16));
        $prefix = $type === 'image' ? 'images/' : 'files/';

        return $prefix . date('Ymd') . '/' . $randomString . '.' . $ext;
    }
}